/*
    Copyright (C) 2011  Hugo Arregui, FuDePAN

    This file is part of the FuDePAN Extensible Cache

    Feca is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Feca is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Feca.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef FECA_LOADING_POLICIES_H
#define FECA_LOADING_POLICIES_H

#include <mili/mili.h>

template<class Cache, class Loader>
class FullLoading
{
    typedef typename Cache::Key Key;
    typedef typename Cache::CacheableObject CacheableObject;
public:
    FullLoading(Cache& _cache) :
        cache(_cache),
        loader(_cache.loader),
        loaded(false)
    {};
    void onKeyRequested(Key k)
    {
        if (!loaded)
        {
            loaded = true;
            bool end;
            do
            {
                CacheableObject o = loader->createEmptyObject(k);
                end = loader->load_next(k, o);
                if (!end)
                {
                    cache.insert(k, o);
                }
            }
            while (!end);
        }
    }
private:
    Cache& cache;
    Loader* const loader;
    bool loaded;
};

template<int size, class Cache, class Loader>
class SlidingWindowLoading
{
    typedef typename Cache::Key Key;
    typedef typename Cache::CacheableObject CacheableObject;
public:
    SlidingWindowLoading(Cache& _cache) :
        cache(_cache),
        loader(_cache.loader),
        window_size(size),
        first_time(true)
    {};
    void onKeyRequested(Key key)
    {
        if (first_time)
        {
            load(loader->next_key());
            first_time = false;
        }
        else if (!(loaded_range.first <= mili::bchain(key) <= loaded_range.second))
        {
            load(key);
        }
    }
private:
    Cache& cache;
    Loader* const loader;
    const unsigned int window_size;
    std::pair<Key, Key> loaded_range;
    bool first_time;
    void load(Key from)
    {
        cache.clear();
        loaded_range.first = from;
        unsigned int i = 0;
        Key k = from;
        bool end;
        do
        {
            CacheableObject o = loader->createEmptyObject(k);
            end = loader->load(k, o) || i >= window_size;
            if (!end)
            {
                cache.insert(k, o);
                loaded_range.second = k;
                k = loader->next_key(k);
                ++i;
            }
        }
        while (!end);
    }
};
#endif
